using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Net.Sockets;

namespace Microsoft.Samples.BizTalk.Adapter.Tcp.Receive
{
    /// <summary>
    ///     This class represents a work item used to submit ordered batches to BizTalk.
    /// </summary>
    /// <author>
    ///     Paul Brown
    /// </author>
    public class OrderedBatchWorkItem : IWorkItem
    {
        #region Private Instance Members

        private WorkItemStatusTypes _status;            // The status of the work item
        private ReceiveLocation     _receiveLocation;   // A reference to the receive location
        private Socket              _clientSocket;      // The client socket that has work
        private Stream[]            _dataStreams;       // The data to be submitted to BizTalk

        #endregion

        #region Constructor

        /// <summary>
        ///     This constructor initializes the class.
        /// </summary>
        public OrderedBatchWorkItem(ReceiveLocation receiveLocation, Socket clientSocket)
        {
            // Validate arguments
            if (receiveLocation == null) throw new ArgumentNullException("receiveLocation");
            if (clientSocket == null) throw new ArgumentNullException("clientSocket");

            // Initialize members
            this._receiveLocation = receiveLocation;
            this._clientSocket = clientSocket;
            this._dataStreams = null;
            this._status = WorkItemStatusTypes.Ready;
        }

        #endregion

        #region Public Instance Properties

        /// <summary>
        ///     This property returns the receive location that produced this work item.
        /// </summary>
        public ReceiveLocation ReceiveLocation
        {
            get { return (this._receiveLocation); }
        }

        /// <summary>
        ///     This property returns the client socket that has work.
        /// </summary>
        public Socket ClientSocket
        {
            get { return (this._clientSocket); }
        }

        /// <summary>
        ///     This property gets or sets the data received from the socket.
        /// </summary>
        public Stream[] DataStreams
        {
            get { return (this._dataStreams); }
            set { this._dataStreams = value; }
        }

        #endregion

        #region Private Instance Methods

        /// <summary>
        ///     This method saves all of the data streams to disk.
        /// </summary>
        private void SaveDataStreams()
        {
            // Loop around each stream
            foreach (Stream stream in this.DataStreams)
            {
                // Ensure stream is repositioned at the beginning
                stream.Position = 0;

                // Write stream contents to disk
                DeadMessage.WriteStreamToDisk(this.ReceiveLocation.Adapter, stream);
            }
        }

        /// <summary>
        ///     This method streams the data off the socket onto disk in binary format.
        /// </summary>
        private void SaveDataOnSocket()
        {
            // Try and stream data off socket onto disk
            using (NetworkStream stream = new NetworkStream(this.ClientSocket, true))
            {
                DeadMessage.WriteStreamToDisk(this.ReceiveLocation.Adapter, stream);
                stream.Close();
            }
        }

        #endregion

        #region IWorkItem Members

        #region Public Instance Properties

        /// <summary>
        ///     This property gets or sets the status of the work item.
        /// </summary>
        public WorkItemStatusTypes Status
        {
            get { return (this._status); }
            set { this._status = value; }
        }

        #endregion

        #region Public Instance Methods

        /// <summary>
        ///     This method saves the content of the work item to the folder location specified by the adapter.
        /// </summary>
        public void Save()
        {
            // Check state of work item
            switch (this.Status)
            {
                case WorkItemStatusTypes.Ready:

                    // Try and stream data off the socket into the file in binary format
                    this.SaveDataOnSocket();
                    break;

                case WorkItemStatusTypes.InProgress:

                    // Determine if the data streams exist
                    if (this.DataStreams != null)
                    {
                        // Save data streams
                        this.SaveDataStreams();
                    }
                    else
                    {
                        // Determine if we can stream data off socket
                        if (this.ClientSocket != null)
                        {
                            if ((this.ClientSocket.Connected) && (this.ClientSocket.Available > 0))
                            {
                                this.SaveDataOnSocket();
                            }
                        }
                    }
                    break;

                case WorkItemStatusTypes.Complete:

                    // Nothing to do
                    break;
            }
        }

        #endregion

        #endregion
    }
}
